package ca.mcgill.schedulability.singleevent;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import ca.mcgill.model.digraph.Digraph;
import ca.mcgill.model.digraph.DigraphEdge;
import ca.mcgill.model.digraph.TransactionDigraph;
import ca.mcgill.model.digraph.utils.DigraphDisplay;
import ca.mcgill.model.digraph.utils.TransactionDigraphUtils;
import ca.mcgill.model.functional.Transition;
import ca.mcgill.model.functional.triggering.ExternalEvent;
import ca.mcgill.model.implementation.ImplementationModel;
import ca.mcgill.model.implementation.Task;
import ca.mcgill.model.implementation.Transaction;
import ca.mcgill.schedulability.singleevent.functional.Bounds;
import ca.mcgill.schedulability.singleevent.functional.DemandTuple;
import ca.mcgill.schedulability.singleevent.functional.DemandTupleSet;
import ca.mcgill.schedulability.singleevent.model.SchedulabilityModel;
import ca.mcgill.schedulability.singleevent.model.SingleEventChain;
import ca.mcgill.schedulability.singleevent.utils.FindBounds;
import ca.mcgill.schedulability.singleevent.utils.dbf;
import ca.mcgill.schedulability.singleevent.utils.rbf;



// TODO list :
// 1) I have to further enhanced the dynamic programming  part
// More specifically, add a field to demand tuples as taken etc
// not to taken an offset edge twice !!
// IDEA: maybe you could use pairs to annotate the triggering vertex
// and the triggered offsetEdge (so not to be taken more than once)


public class SchedulabilityAnalysis {


	public boolean isSchedulable(Map<Task, Digraph> taskToDigraph, ImplementationModel implModel) {
		
		
		FindBounds findBounds = new FindBounds();
		
		// create a dummy DTset to reinitialized multiple times later
		// create a dummy DTset to check 
		List<Boolean>  dummyDTboolean = new ArrayList<Boolean>();
		boolean addTrue = true; dummyDTboolean.add(addTrue);	
		
		DemandTuple dummyDT = new DemandTuple(-1, -1, null, dummyDTboolean); // initialOffsetZero
		List<DemandTuple>  dummyDTset = new ArrayList<DemandTuple>();
		dummyDTset.add(dummyDT);
		DemandTupleSet myDTset = new DemandTupleSet(dummyDTset);
		
		rbf rbf = new rbf();
		dbf dbf = new dbf();
		DigraphDisplay display = new DigraphDisplay();

		
		// for all transactions we will check the last task !!
		for (Transaction transaction : implModel.getTransactions()) {
		
			// check up to time Tmax (to inspect how to get its value) -- go every +period time units
			ExternalEvent myEvent;
			myEvent = (ExternalEvent) transaction.getTasksInTransaction().get(0).getFsm().getTransitions().get(0).getTrigE();
			double myPeriod = (double) myEvent.getPeriod();		
			
			double tmax = 100;

			for (double t=myPeriod;t<=tmax;t+=myPeriod){
				
				double ihpSum = 0, dbfSum = 0;  
					
				// Check all other transactions : check how they interfere with last task of transaction
				for (Transaction transactionIn : implModel.getTransactions()) {
										
			
					// find bounds for phases of all chains T relatively to last task of Ti
					Bounds bounds = findBounds.findBounds(transactionIn, transaction);
			
					
					// does it actually interfere?
					if (bounds.doesInterfere()){
					
						// for the guys of the same transaction compute their dbfs
						if (transaction.equals(transactionIn)){

							// for all task within Lower - Upper Bounds 
							for (int taskCnt=bounds.getLowerBound();taskCnt<=bounds.getUpperBound();taskCnt++){
								
								Task task = transactionIn.getTasksInTransaction().get(taskCnt);
								// get the Digraph of current interest 
								//Digraph currentDigraph =  transactionIn.getTasksInTransaction(). T.getImplDigraphs().get(taskCnt);
								
								Digraph currentDigraph =	taskToDigraph.get(task);
								// reset to have the proper Dtset per case
								myDTset.resetDemandTripleSet(currentDigraph);
	
								// display.printoutDigraphWithWCET(currentDigraph);
								
								// compute rbf of the current one	
								double currentDbf = dbf.dbf(t, currentDigraph, myDTset);
								//System.out.println("dbf at t = " + t + " is " + currentDbf);
								dbfSum += currentDbf;
								
							}
							
						}else{ // for the other transactions, compute rbf as before
													
							// for all task within Lower - Upper Bounds 
							for (int taskCnt=bounds.getLowerBound();taskCnt<=bounds.getUpperBound();taskCnt++){
								
								
								Task task = transactionIn.getTasksInTransaction().get(taskCnt);
								// get the Digraph of current interest 
								//Digraph currentDigraph =  transactionIn.getTasksInTransaction(). T.getImplDigraphs().get(taskCnt);
								
								Digraph currentDigraph =	taskToDigraph.get(task);
								// reset to have the proper Dtset per case
								myDTset.resetDemandTupleSet(currentDigraph);
	
								// display.printoutDigraphWithWCET(currentDigraph);
								
								// compute rbf of the current one	
								double currentRbf = rbf.rbf(t, currentDigraph, myDTset);
								//System.out.println("rbf at t = " + t + " is " + currentRbf);
								ihpSum += currentRbf;
								
							}
							
						}
					}// if does interfere
				} // for all transactions
			
		
				//System.out.println("Sum_rbf at t = " + t + " is " + ihpSum);

				
				System.out.println(" t " + t + " - ihpSum " + ihpSum + " >= dbfSum " + dbfSum);
				// evaluate Baruah's condition at times t = 1*period, 2*period, ...
				if (!(t - ihpSum >= dbfSum)){
					return false;
				}

			}
		} // for all \tau (last Tasks of all chains)
			
		return true;
	}
	
	
	// dstam : I created V2 05/09 to capture the fact that 
	// another transaction might have or not multiple instances... !!!
	// hint --> use the multipleTransc boolean of bounds
	
	// also update it to check the schedulability every "+deadline" time units

	
	public boolean isSchedulableV2(Map<Task, Digraph> taskToDigraph, ImplementationModel implModel) {
		
		
		FindBounds findBounds = new FindBounds();
		
		// create a dummy DTset to reinitialized multiple times later
		// create a dummy DTset to check 
		List<Boolean>  dummyDTboolean = new ArrayList<Boolean>();
		boolean addTrue = true; dummyDTboolean.add(addTrue);	
		
		DemandTuple dummyDT = new DemandTuple(-1, -1, null, dummyDTboolean); // initialOffsetZero
		List<DemandTuple>  dummyDTset = new ArrayList<DemandTuple>();
		dummyDTset.add(dummyDT);
		DemandTupleSet myDTset = new DemandTupleSet(dummyDTset);
		
		rbf rbf = new rbf();
		dbf dbf = new dbf();
		DigraphDisplay display = new DigraphDisplay();

		
		// for all transactions we will check the last task !!
		for (Transaction transaction : implModel.getTransactions()) {
		
			// check up to time Tmax (to inspect how to get its value) -- go every +deadline time units
			double myDeadline = transaction.getDeadline();
			
			double tmax = 100;

			for (double t=myDeadline;t<=tmax;t+=myDeadline){
				
				double ihpSum = 0, dbfSum = 0;  
					
				// Check all other transactions : check how they interfere with last task of transaction
				for (Transaction transactionIn : implModel.getTransactions()) {
										
			
					// find bounds for phases of all chains T relatively to last task of Ti
					Bounds bounds = findBounds.findBoundsV2(transactionIn, transaction);
			
					
					// does it actually interfere?
					if (bounds.doesInterfere()){
					
						// for the guys of the same transaction compute their dbfs
						if (transaction.equals(transactionIn)){

							// for all task within Lower - Upper Bounds 
							for (int taskCnt=bounds.getLowerBound();taskCnt<=bounds.getUpperBound();taskCnt++){
								
								Task task = transactionIn.getTasksInTransaction().get(taskCnt);
								// get the Digraph of current interest 
								//Digraph currentDigraph =  transactionIn.getTasksInTransaction(). T.getImplDigraphs().get(taskCnt);
								
								Digraph currentDigraph =	taskToDigraph.get(task);
								// reset to have the proper Dtset per case
								myDTset.resetDemandTripleSet(currentDigraph);
	
								// display.printoutDigraphWithWCET(currentDigraph);
								
								// compute rbf of the current one	
								double currentDbf = dbf.dbf(t, currentDigraph, myDTset);
								//System.out.println("dbf at t = " + t + " is " + currentDbf);
								dbfSum += currentDbf;
								
							}
							
						}else{ // for the other transactions, compute rbf as before
													
							// for all task within Lower - Upper Bounds 
							for (int taskCnt=bounds.getLowerBound();taskCnt<=bounds.getUpperBound();taskCnt++){
								
								
								Task task = transactionIn.getTasksInTransaction().get(taskCnt);
								// get the Digraph of current interest 
								//Digraph currentDigraph =  transactionIn.getTasksInTransaction(). T.getImplDigraphs().get(taskCnt);
								
								Digraph currentDigraph =	taskToDigraph.get(task);
								// reset to have the proper Dtset per case
								myDTset.resetDemandTupleSet(currentDigraph);
	
								// display.printoutDigraphWithWCET(currentDigraph);
								
								// compute rbf of the current one	
								// if you can have multiple transactions then go for the rbf computation
								// otherwise you could have 1 instance of the transaction, 
								// so you could just keep the WCET of all Digraphs
								double currentRbf;
								
								if (!bounds.multipleTransc()){
									
									currentRbf = 0;
									// get the max of all DT sets -- line 14 of stigge
									for (DemandTuple DTmax : myDTset.getDemandTuples()) {
										if (currentRbf < DTmax.getRequestTime()){
											currentRbf = DTmax.getRequestTime();
										}
									}
									
								}else{
								
									currentRbf = rbf.rbf(t, currentDigraph, myDTset);
									//System.out.println("rbf at t = " + t + " is " + currentRbf);
									
								}
								ihpSum += currentRbf;
								
							}
							
						}
					}// if does interfere
				} // for all transactions
			
		
				//System.out.println("Sum_rbf at t = " + t + " is " + ihpSum);

				
				System.out.println(" t " + t + " - ihpSum " + ihpSum + " >= dbfSum " + dbfSum);
				// evaluate Baruah's condition at times t = 1*period, 2*period, ...
				if (!(t - ihpSum >= dbfSum)){
					return false;
				}

			}
		} // for all \tau (last Tasks of all chains)
			
		return true;
	}
	
	
	

	public boolean isSchedulableOptimized(Map<Task, Digraph> taskToDigraph, ImplementationModel implModel) {
		
		FindBounds findBounds = new FindBounds();
		TransactionDigraphUtils trnsDigUtils = new TransactionDigraphUtils();
		rbf rbf = new rbf();
		dbf dbf = new dbf();
		DigraphDisplay display = new DigraphDisplay();
		
		
		// create a dummy DTset to reinitialized multiple times later
		// create a dummy DTset to check 
		List<Boolean>  dummyDTboolean = new ArrayList<Boolean>();
		boolean addTrue = true; dummyDTboolean.add(addTrue);	
		
		DemandTuple dummyDT = new DemandTuple(-1, -1, null, dummyDTboolean); // initialOffsetZero
		List<DemandTuple>  dummyDTset = new ArrayList<DemandTuple>();
		dummyDTset.add(dummyDT);
		DemandTupleSet myDTset = new DemandTupleSet(dummyDTset);
		
	
		// for all transactions we will check the last task !!
		for (Transaction transaction : implModel.getTransactions()) {
			
			// repeat this for each iteration cause you process them in between
			Map<Transaction, TransactionDigraph> transactionDigraphs = trnsDigUtils
					.createTransactionDigraphs(implModel, taskToDigraph);
		
			// check up to time Tmax (to inspect how to get its value) -- go every +period time units
			ExternalEvent myEvent;
			myEvent = (ExternalEvent) transaction.getTasksInTransaction().get(0).getFsm().getTransitions().get(0).getTrigE();
			double myPeriod = (double) myEvent.getPeriod();		
			
			double tmax = 100;

			for (double t=myPeriod;t<=tmax;t+=myPeriod){
				
				double ihpSum = 0, dbfSum = 0;  
					
				// Check all other transactions : check how they interfere with last task of transaction
				for (Transaction transactionIn : implModel.getTransactions()) {
										
					
					// pre-processing the transaction digraph - just the test
					Random rand = new Random();
					Transaction trnsToChange = transactionIn;
					Transaction trnsToAnalyse = transaction;

					trnsDigUtils.trnsDigPreprocessing(
							transactionDigraphs.get(trnsToChange), taskToDigraph,
							trnsToAnalyse.getTasksInTransaction().get(0).getPriority());
					
			
					// find bounds for phases of all chains T relatively to last task of Ti
					Bounds bounds = findBounds.findBounds(transactionIn, transaction);
			
					
					// does it actually interfere?
					if (bounds.doesInterfere()){
					
						// for the guys of the same transaction compute their dbfs
						if (transaction.equals(transactionIn)){

							// for all task within Lower - Upper Bounds 
							for (int taskCnt=bounds.getLowerBound();taskCnt<=bounds.getUpperBound();taskCnt++){
								
								Task task = transactionIn.getTasksInTransaction().get(taskCnt);
								// get the Digraph of current interest 
								//Digraph currentDigraph =  transactionIn.getTasksInTransaction(). T.getImplDigraphs().get(taskCnt);
								
								Digraph currentDigraph =	taskToDigraph.get(task);
								// reset to have the proper Dtset per case
								myDTset.resetDemandTripleSet(currentDigraph);
	
								// display.printoutDigraphWithWCET(currentDigraph);
								
								// compute rbf of the current one	
								double currentDbf = dbf.dbf(t, currentDigraph, myDTset);
								//System.out.println("dbf at t = " + t + " is " + currentDbf);
								dbfSum += currentDbf;
								
							}
							
						}else{ // for the other transactions, compute rbf as before

							Digraph currentDigraph = transactionDigraphs.get(transactionIn);
							myDTset.resetDemandTupleSet(currentDigraph);
	
							// compute rbf of the current transaction - given transaction Digraph	
							double currentRbf = rbf.rbf(t, currentDigraph, myDTset);
							//System.out.println("rbf of Digraph at t = " + t + " is " + currentRbf);
							ihpSum += currentRbf;
							
						}
					}// if does interfere
				} // for all transactions
			
		
				//System.out.println("Sum_rbf at t = " + t + " is " + ihpSum);
				
				System.out.println(" t " + t + " - ihpSum " + ihpSum + " >= dbfSum " + dbfSum);
				// evaluate Baruah's condition at times t = 1*period, 2*period, ...
				if (!(t - ihpSum >= dbfSum)){
					return false;
				}

			}
						
			// now reset the Map to reinitialize it again
			transactionDigraphs.clear();
			
		} // for all \tau (last Tasks of all chains)
			
		return true;
	}
	
	


	
}
